/**
 * Copyright (c) 2018-2025, NXOS Development Team
 * SPGX-License-Identifier: Apache-2.0
 *
 * Contains: 74HC165 键盘驱动，当前支持16个按键
 *
 * Change Logs:
 *	2023/07/21	增加文件操作接口，使用队列优化read操作
 *	2023/07/16  修改获取按键值算法，提高效率，增加默认按键功能
 *	2023/07/11	完成按键驱动结构定义，基础功能实现
 * Date					Author			Notes
 * 2023-07-21		wangzhiming		key board driver
 */
#define MODEL_TAG "KeyBoard"
#define DRV_NAME "f133 joystick"
#define DEV_NAME "joystick0"

#include <nxos.h>

#include <drivers/iic.h>
#include <drivers/gpio.h>
#include <drivers/pin.h>
#include <regs.h>
#include <base/log.h>
#include <base/driver.h>
#include <base/string.h>
#include <base/clock.h>
#include <base/thread.h>
#include <base/string.h>
#include <drvfw/input_event.h>

#define key_loge(fmt, arg...) \
	NX_LOG_E("E/%s <%s %4d> "fmt, MODEL_TAG, __FUNCTION__, __LINE__, ##arg)

#if DEBUG
#define key_logd(fmt, arg...) \
	NX_LOG_D("D/%s <%s %4d> "fmt, MODEL_TAG, __FUNCTION__, __LINE__, ##arg)
#else
#define key_logd(fmt, arg...)
#endif

#define key_logi(fmt, arg...) \
	NX_LOG_I("I/%s <%s %4d> "fmt, MODEL_TAG, __FUNCTION__, __LINE__, ##arg)

#define	set_bit(x, bit)	    (x |= (1 << bit))
#define	clear_bit(x, bit)	(x &= ~(1 << bit))
#define	get_bit(x, bit)	    ((x & (1 << bit)) >> bit)

#define write_reg(x, addr, bit) do {\
				if (x) \
					set_bit(*addr, bit); \
				else \
					clear_bit(*addr, bit); \
			} while(0)

#define KBD_EVENT_QUEUE_MAX_SZ  32

/**
 * 键盘引脚定义
*/
#define CLK_PIN		"PG.13"
#define CS_PIN		"PG.14"
#define MISO_PIN	"PG.15"
#define KEY_NUM_MAX	16

// 定义空指针
#define NULL 		((void*)0)

/**
 * 按键功能枚举
*/
typedef enum {
	KEY_FUNCTION_UP = 0,
	KEY_FUNCTION_DOWN,
	KEY_FUNCTION_RIGHT,
	KEY_FUNCTION_LEFT,
	KEY_FUNCTION_A,
	KEY_FUNCTION_B,
	KEY_FUNCTION_X,
	KEY_FUNCTION_Y,
	KEY_FUNCTION_HOME,
	KEY_FUNCTION_ENT,
	KEY_FUNCTION_RET,
	KEY_FUNCTION_MAX,
	KEY_FUNCTION_NONE = -1
}KEY_FUNCTION;

/**
 * 按键按下状态
*/
typedef enum {
	KEY_VALUE_PRESS	= 0,
	KEY_VALUE_RELEASE,
	KEY_VALUE_NONE,
	KEY_VALUE_MAX
}KEY_VALUE;

typedef struct spi_handle {
	NX_IArch	clk;
	NX_IArch	cs;
	NX_IArch	miso;
	NX_IArch	mosi;
	char *owner;
}spi_handle_t;

typedef struct key_map {
	NX_U16 value;
	KEY_FUNCTION func;
} key_map_t;

typedef struct key_handle {
	char name[16];
	NX_U16 key_num;

	key_map_t *keys;
	spi_handle_t spi;

	NX_U32		thread_start;
	NX_Device *device;
	NX_Driver *driver;
	NX_Thread *thread;
	NX_TimeVal time_table[KEY_FUNCTION_MAX];
	char key_pressed[KEY_FUNCTION_MAX];
	

    NX_InputEventQueue eventQueue;
}key_handle_t;

/**
 * 按键功能字符串
*/
static const char *key_name[KEY_FUNCTION_MAX] = {
	"UP",
	"DOWN",
	"RIGHT",
	"LEFT",
	"A",
	"B",
	"X",
	"Y",
	"HOME",
	"ENTER",
	"RETURN"
};



static key_handle_t *key_control = NULL;

static void default_func_to_key(key_map_t *key)
{
	if (!key)
		goto exit;

	key[0].func = KEY_FUNCTION_RET;
	key[0].value = 0;
	key[1].func = KEY_FUNCTION_ENT;
	key[1].value = 1;
	key[2].func = KEY_FUNCTION_LEFT;
	key[2].value = 2;
	key[3].func = KEY_FUNCTION_RIGHT;
	key[3].value = 3;
	key[4].func = KEY_FUNCTION_DOWN;
	key[4].value = 4;
	key[5].func = KEY_FUNCTION_UP;
	key[5].value = 5;
	key[10].func = KEY_FUNCTION_HOME;
	key[10].value = 10;
	key[11].func = KEY_FUNCTION_Y;
	key[11].value = 11;
	key[12].func = KEY_FUNCTION_X;
	key[12].value = 12;
	key[13].func = KEY_FUNCTION_B;
	key[13].value = 13;
	key[14].func = KEY_FUNCTION_A;
	key[14].value = 14;

	key[6].func = KEY_FUNCTION_NONE;
	key[6].value = KEY_VALUE_NONE;
	key[7].func = KEY_FUNCTION_NONE;
	key[7].value = KEY_VALUE_NONE;
	key[8].func = KEY_FUNCTION_NONE;
	key[8].value = KEY_VALUE_NONE;
	key[9].func = KEY_FUNCTION_NONE;
	key[9].value = KEY_VALUE_NONE;
	key[15].func = KEY_FUNCTION_NONE;
	key[15].value = KEY_VALUE_NONE;
	return;
exit:
	key_loge("key is nullptr");
}

static key_handle_t *get_key_board_handle(void)
{
	if (!key_control) {
		key_loge("key_control is not initialize");
		return NULL;
	}
	return key_control;
}

static NX_U16 spi_read_byte(spi_handle_t *spi_handle)
{
	NX_U16 keybit = 0;

	d1s_pin_write(spi_handle->cs, 1);

	for (NX_U16 i = 0; i < KEY_NUM_MAX; i++) {
		d1s_pin_write(spi_handle->clk, 0);
		d1s_pin_write(spi_handle->clk, 1);
		NX_U16 dat_in = d1s_pin_read(spi_handle->miso);
		write_reg(dat_in, &keybit, i);
	}

	d1s_pin_write(spi_handle->cs, 0);

	return keybit;
}

static KEY_VALUE get_key_val_index(NX_U16 val, NX_U8 index)
{
	NX_U16 rd_val;
	KEY_VALUE ret_val;

	/**
	 * 硬件设计上这几个按键默认拉低，
	 * 减轻后级框架负担，这里直接屏蔽掉
	*/
	if ((index > 5 && index < 10) || index == 15)
		return KEY_VALUE_NONE;

	rd_val = val;
	if (!get_bit(rd_val, index)) {
		ret_val = KEY_VALUE_PRESS;
	} else
		ret_val = KEY_VALUE_RELEASE;

	return ret_val;
}

static NX_U32 calibrate_keyboard(key_handle_t *handle, NX_U8 initlize)
{
	NX_U16 index = 0;
	NX_U16 flag = 0xffff;
	KEY_VALUE cur_val = KEY_VALUE_RELEASE;
	KEY_FUNCTION key_func = KEY_FUNCTION_UP;

	if (!handle)
		goto exit;

	/**
	 * 如果在驱动加载时调用，需要延时下，避免内核其他打印
	*/
	if (initlize) {
		NX_ThreadSleep(2000);
	}

	// 每次进行校准键盘功能时清空上一次校准结果
	for (NX_U16 i = 0; i < handle->key_num; i++) {
		handle->keys[i].func = KEY_FUNCTION_NONE;
		handle->keys[i].value = KEY_VALUE_NONE;
	}

again:
	NX_Printf("Please enter <%s> \n", key_name[index]);
	while (1) {
		for (NX_U16 i = 0; i < handle->key_num; i++) {
			cur_val = get_key_val_index(spi_read_byte(&handle->spi), i);
			if (cur_val == KEY_VALUE_PRESS && get_bit(flag, i)) {
				handle->keys[i].func = key_func;
				handle->keys[i].value = i;
#ifdef DEBUG
				NX_Printf("key%d press val:%d\n", i, cur_val);
				NX_Printf("value:%d index:%d func:%d\n", \
						handle->keys[i].func, index, key_func);
#endif
				index++;
				key_func++;
				clear_bit(flag, i);
				/**
				 * 只有当所有功能都有按键与之对应才会退出
				*/
				if (index == KEY_FUNCTION_MAX)
					goto out;

				goto again;
			} else if (cur_val == KEY_VALUE_RELEASE)
				set_bit(flag, i);
		}
		NX_ThreadSleep(10);
	}
out:
	/**
	 * 校准结束后输出按键值与功能的映射关系
	*/
	NX_Printf("\nfunc  -> key\n");
	for (KEY_FUNCTION func = KEY_FUNCTION_UP; func < KEY_FUNCTION_MAX; func++){
		for (NX_U16 j = 0; j < handle->key_num; j++)
			if (func == handle->keys[j].func && \
				handle->keys[j].func != KEY_FUNCTION_NONE)
				NX_Printf("%6s:%2d\n", key_name[func], handle->keys[j].value);
	}

	return NX_EOK;
exit:
	key_loge("The key has not been initialized");
	return NX_ERROR;
}

static void func_to_event(NX_InputEvent *e, KEY_FUNCTION func, KEY_VALUE val)
{
	e->type = NX_EV_KEY;

	if (val == KEY_VALUE_PRESS)
	{
		e->value = 1;
	}
	else
	{
		e->value = 0;
	}

	switch (func) {
	case KEY_FUNCTION_UP:
		e->code = NX_KEY_UP;
		break;
	case KEY_FUNCTION_DOWN:
		e->code = NX_KEY_DOWN;
		break;
	case KEY_FUNCTION_RIGHT:
		e->code = NX_KEY_RIGHT;
		break;
	case KEY_FUNCTION_LEFT:
		e->code = NX_KEY_LEFT;
		break;
	case KEY_FUNCTION_A:
		e->code = NX_KEY_A;
		break;
	case KEY_FUNCTION_B:
		e->code = NX_KEY_B;
		break;
	case KEY_FUNCTION_X:
		e->code = NX_KEY_X;
		break;
	case KEY_FUNCTION_Y:
		e->code = NX_KEY_Y;
		break;
	case KEY_FUNCTION_HOME:
		e->code = NX_KEY_ESCAPE;
		break;
	case KEY_FUNCTION_ENT:
		e->code = NX_KEY_ENTER;
		break;
	case KEY_FUNCTION_RET:
		e->code = NX_KEY_BACKSPACE;
		break;
	default:
		e->code = NX_KEY_UNKNOWN;
	}
}

static KEY_FUNCTION key_to_func(NX_U16 index)
{
	key_handle_t *ptr = get_key_board_handle();
	KEY_FUNCTION retval = KEY_FUNCTION_NONE;

	if (!ptr)
		goto exit;

	if (index < ptr->key_num) {
		retval = ptr->keys[index].func;
	}

	return retval;
exit:
	key_loge("The key has not been initialized");
	return KEY_FUNCTION_NONE;
}

#define ONEKEY_DELAY 50 

static void ReportKey(key_handle_t *handle, KEY_FUNCTION func, KEY_VALUE val)
{
	NX_InputEvent e;
	int report = 1;

	if (val == KEY_VALUE_PRESS)
	{
		// NX_LOG_W("press:%d", func);
		if (handle->time_table[func] != 0)
		{
			if (NX_ClockGetMillisecond() - handle->time_table[func] < ONEKEY_DELAY)
			{
				report = 0;
			}
			else
			{
				// NX_LOG_W("press:%d -> %s", func, key_name[func]);
				handle->time_table[func] = 0;
			}
		}

		if (!handle->time_table[func])
		{
			handle->time_table[func] = NX_ClockGetMillisecond();
			handle->key_pressed[func] = 1;
		}
	}

	if (report)
	{
		func_to_event(&e, func, val);	// 将原始按键数据转换成标准输入事件
		NX_InputEventQueuePut(&handle->eventQueue, &e); // 推送到队列
	}
}

static void NX_KeyInputThread(void *arg)
{
	key_handle_t *internal = (key_handle_t*)arg;
	KEY_VALUE val;
	KEY_FUNCTION func;
	NX_U32 func_bits;
	NX_U32 press_bits;
	NX_U32 release_bits;

	while (1) {
		func_bits = 0;
		press_bits = 0;
		release_bits = 0;
		
		/* 扫描所有按键，记录按键状态 */
		for (NX_U16 index = 0; index < internal->key_num; index++) {
			val = get_key_val_index(spi_read_byte(&internal->spi), index);
			if (val == KEY_VALUE_PRESS) {
				func = key_to_func(index);
#ifdef DEBUG
				key_logd("<%s> press\n", key_name[func]);
#endif
				set_bit(func_bits, func);
				set_bit(press_bits, func);
				continue;
			}
			else if (val == KEY_VALUE_RELEASE)
			{
				func = key_to_func(index);
				if (internal->key_pressed[func] == 1)
				{
					internal->key_pressed[func] = 0;
					// NX_LOG_W("release:%d -> %s", func, key_name[func]);
#ifdef DEBUG
					key_logd("<%s> release\n", key_name[func]);
#endif
					set_bit(func_bits, func);
					set_bit(release_bits, func);
				}
			}
		}

		/* 为了支持同时按下多个按键，需要先扫描完后，再单独处理 */
		if ((press_bits != 0 || release_bits != 0) && func_bits != 0)
		{
			/* press key */
			for (NX_U16 index = 0; index < KEY_FUNCTION_MAX; index++) {
				if (get_bit(func_bits, index) && get_bit(press_bits, index))
				{
					ReportKey(internal, index, KEY_VALUE_PRESS);
				}
			}
			/* release key */
			for (NX_U16 index = 0; index < KEY_FUNCTION_MAX; index++) {
				if (get_bit(func_bits, index) && get_bit(release_bits, index))
				{
					ReportKey(internal, index, KEY_VALUE_RELEASE);
				}
			}
		}
		NX_ThreadSleep(10);
	}
}

static NX_Error key_ops_read(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
	NX_InputEvent e;
	key_handle_t *internal = device->extension;

	NX_ASSERT(internal);

	if (!len || len != sizeof(NX_InputEvent))
		return NX_EINVAL;

	if (NX_InputEventQueueGet(&internal->eventQueue, &e) != NX_EOK) {
		return NX_EAGAIN;
	}
	NX_MemCopy(buf, &e, sizeof(NX_InputEvent));

	if (outLen)
		*outLen = len;

	return NX_EOK;
}

static NX_Error key_ops_ioctl(struct NX_Device *device, NX_U32 cmd, void *arg)
{
	key_handle_t *ext;
	NX_U32 ret = 0;

	// key_logi("Enter!\n");

	ext = device->extension;

	NX_ASSERT(ext);
	if (!key_control)
		return NX_ERROR;

	switch (cmd) {
	case NX_INPUT_EVENT_CMD_CALIBRATE:
		ret = calibrate_keyboard(ext, 0);
		NX_MemCopy(arg, &ret, sizeof(NX_U32));
		break;
	default:
		return NX_EINVAL;
	}

	// key_logi("done!");
	return NX_EOK;
}

NX_Error key_ops_open(struct NX_Device *device, NX_U32 flags)
{
	key_handle_t *key_ctl = device->extension;
	static NX_U32 start = 1;
	NX_Error ret;
	NX_ASSERT(key_ctl);

	// key_logi("enter!");
	if (key_ctl->thread_start) {
		key_ctl->thread_start = 0;
		if (key_ctl->thread) {
			ret = NX_ThreadStart(key_ctl->thread);
			if (ret != NX_EOK) {
				key_loge("thread start failed! ret:%d", ret);
				return NX_ERROR;
			}
		}
	} else {
		ret = NX_ThreadBlock(key_ctl->thread);
		if (ret != NX_EOK) {
			key_loge("thread block failed! ret:%d", ret);
			return NX_ERROR;
		}
	}

	// key_logi("done!");
	return NX_EOK;
}

NX_Error key_ops_close(struct NX_Device *device)
{
	key_handle_t *key_ctl = device->extension;
	NX_Error ret;
	NX_ASSERT(key_ctl);

	// key_logi("enter!");
	if (key_ctl->thread) {
		ret = NX_ThreadBlock(key_ctl->thread);
		if (ret != NX_EOK) {
			key_loge("thread unblock failed! ret:%d", ret);
			return NX_ERROR;
		}
	}
	// key_logi("done!");
	return NX_EOK;
}

NX_PRIVATE NX_Error key_ops_poll(struct NX_Device *device, NX_PollState * pState)
{
    key_handle_t * ext = device->extension;

    if (NX_InputEventQueueEmpty(&ext->eventQueue))
    {
        return NX_ENORES;
    }

    pState->outmask = NX_POLL_READ;

    return NX_EOK;
}

static NX_DriverOps KeyboardDriverOps = {
	.open		= key_ops_open,
	.close		= key_ops_close,
    .read       = key_ops_read,
    .control    = key_ops_ioctl,
	.poll 		= key_ops_poll,
};

static void key_board_init(void)
{
	spi_handle_t gpio;

	key_control = (key_handle_t*)NX_MemAlloc(sizeof(key_handle_t));
	if (!key_control) {
		key_loge("no enought memory");
		goto exit;
	}
	NX_MemSet(key_control, 0, sizeof(key_handle_t));

	key_control->driver = NX_DriverCreate(DRV_NAME, NX_DEVICE_TYPE_INPUT, 0, &KeyboardDriverOps);
	if (key_control->driver == NX_NULL) {
		key_loge("create driver failed!");
		goto exit1;
	}

	if (NX_DriverAttachDevice(key_control->driver, DEV_NAME, &key_control->device) != NX_EOK) {
        key_loge("attach device %s failed!", DEV_NAME);
        goto exit2;
    }

	if (NX_DriverRegister(key_control->driver) != NX_EOK) {
		key_loge("register driver failed");
		goto exit3;
	}

	key_control->device->extension = key_control;

	// gpio初始化
	gpio.clk  = d1s_pin_get(CLK_PIN);
	gpio.cs   = d1s_pin_get(CS_PIN);
	gpio.miso = d1s_pin_get(MISO_PIN);
	gpio.mosi = -1;
	gpio.owner = "key board";

	if (gpio.clk == -NX_EINVAL ||\
		gpio.cs  == -NX_EINVAL ||\
		gpio.miso== -NX_EINVAL) {

		key_loge("gpio request failed!");
		goto exit4;
	}

	// 设置gpio工作模式
	d1s_pin_mode(gpio.clk, PIN_MODE_OUTPUT);
	d1s_pin_mode(gpio.cs, PIN_MODE_OUTPUT);
	d1s_pin_mode(gpio.miso, PIN_MODE_INPUT_PULLUP);

	/**
	 * 设置输出引脚的默认电平，cs输出低，可以使74HC165处于并行输入状态
	 * 在进行第一次读取时便可以读取到信息
	*/
	d1s_pin_write(gpio.cs, 0);
	d1s_pin_write(gpio.clk, 0);

	key_control->spi = gpio;
	key_control->key_num = KEY_NUM_MAX;
	NX_StrCopyN(key_control->name, "key_board", sizeof("key_board"));

	key_control->keys =  (key_map_t *)NX_MemAlloc(sizeof(key_map_t) * key_control->key_num);
	if (!key_control->keys) {
		key_loge("no enought memory");
		goto exit4;
	}

	key_logi(" -> clk:%d miso:%d mosi:%d cs:%d", gpio.clk, gpio.miso, gpio.mosi, gpio.cs);
	key_logi(" -> %d", key_control->key_num);

    NX_Error ret = NX_InputEventQueueInit(&key_control->eventQueue, KBD_EVENT_QUEUE_MAX_SZ);
	if (ret != NX_EOK) {
		key_loge("queue init failed!");
		goto exit5;
	}
	/**
	 * 由于是使用了并转串芯片，无法获取每个按键中断
	 * 所以这里使用一个线程轮询每个按键状态
	*/
	key_control->thread_start = 1;
	key_control->thread = NX_ThreadCreate("key_input", NX_KeyInputThread, (void*)key_control, NX_THREAD_PRIORITY_HIGH);
	if (!key_control->thread) {
		goto exit6;
	}

	// 默认设置按键值对应的功能
	default_func_to_key(key_control->keys);

	key_logi("drv init successed");

	return;
exit6:
	NX_InputEventQueueExit(&key_control->eventQueue);
exit5:
	NX_MemFree(key_control->keys);
exit4:
	NX_DriverUnregister(key_control->driver);
exit3:
	NX_DriverDetachDevice(key_control->driver, DEV_NAME);
exit2:
	NX_DriverDestroy(key_control->driver);
exit1:
	NX_MemFree(key_control);
exit:
	return;
}

static void key_board_exit(void)
{
	key_handle_t *key_handle;

	key_handle = get_key_board_handle();
	if (key_handle) {
		NX_InputEventQueueExit(&key_handle->eventQueue);
		NX_DriverUnregister(key_handle->driver);
		NX_DriverDetachDevice(key_handle->driver, DEV_NAME);
        NX_DriverDestroy(key_handle->driver);
		NX_ThreadDestroy(key_handle->thread);
		NX_MemFree(key_handle->keys);
		NX_MemFree(key_handle);
	}

	key_logi("drv exit");
}

NX_DRV_INIT(key_board_init);
NX_DRV_EXIT(key_board_exit);
